package com.ljg.activiti.c_processinstance;

import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipInputStream;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 流程实例、执行对象、任务 相关测试类
 * @author guo
 *
 */
public class ProcessInstanceTest {
	
	private Logger log = LoggerFactory.getLogger(ProcessInstanceTest.class);
	
	// 流程引擎对象
	private ProcessEngine processEngine;
	
	@Before
	public void before() {
		/**
		 * 创建默认的流程引擎对象，默认读取classpath下的activiti.cfg.xml创建流程引擎对象
		 */
		processEngine = ProcessEngines.getDefaultProcessEngine();
		log.info("创建流程引擎对象成功！");
	}
	
	/**
	 * 部署流程定义-从zip
	 * 部署完成之后，数据库表的变化
	 * 	-> `act_ge_bytearray`资源表中插入数据来保存流程的bpmn文件和png文件
	 *  -> `act_ge_property`配置表中会更新数据，下次insert数据的Id
	 *  -> `act_re_deployment`流程部署表中会插入数据，流程部署的ID，名称，部署时间等
	 *  -> `act_re_procdef`流程定义表中会插入数据，流程定义Id，流程定义Key，流程定义的名称，流程定义的版本，部署Id和资源文件名称等信息
	 *  
	 *  processEngine.getRepositoryService()：获取流程定义和流程部署相关的Service
	 */
	@Test
	public void deploymentProcessDefinition_zip() {
		InputStream in = this.getClass().getClassLoader().getResourceAsStream("diagrams/LeaveProcess.zip");
		ZipInputStream zipInputStream = new ZipInputStream(in);
		Deployment deployment = processEngine.getRepositoryService() // 获得与流程定义和部署相关的Service
				.createDeployment() // 创建部署对象
				.addZipInputStream(zipInputStream) // 从zip文件加载
				.name("流程定义测试zip-请假流程") // 设置部署名称
				.deploy(); // 完成流程部署
		log.info("部署完成，deploymentId=" + deployment.getId() + "，deploymentName=" + deployment.getName() + "，deploymentTime=" + deployment.getDeploymentTime());
	}
	
	/**
	 * 启动流程实例
	 * 启动流程实例之后，数据库表的变化
	 *  -> `act_ge_property`配置表中会更新数据，下次insert数据的Id
	 *  -> `act_hi_taskinst`历史任务表中会插入数据，流程定义Id，任务定义Id，流程实例Id，执行对象Id，任务名称，任务办理人，任务开始时间等信息
	 *  -> `act_hi_procinst`历史流程实例表中插入数据，流程实例Id，流程定义Id，开始时间，开始活动定义Id等信息
	 *  -> `act_hi_actinst`历史活动实例表中插入数据，流程定义Id，流程实例Id，执行对象Id，活动定义Id，任务Id，活动定义名称，任务办理人，开始结束时间等信息
	 *  -> `act_hi_identitylink`历史办理人表插入数据，组Id，办理人类型，办理人Id，流程实例Id等信息
	 *  -> `act_ru_execution`正在执行的执行对象表插入数据，执行对象Id，流程实例Id，流程定义Id，活动定义Id等信息
	 *  -> `act_ru_task`正在执行的任务表插入数据，任务Id，执行对象Id，流程实例Id，流程定义Id，当前任务名称，当前任务定义Id，当前任务办理人，创建时间等信息
	 *  -> `act_ru_identitylink`正在执行的办理人表插入数据，组Id，办理人类型，办理人Id，流程实例Id等信息
	 *  
	 * processEngine.getRuntimeService() // 获得正在执行的流程实例和执行对象相关的Service
	 */
	@Test
	public void startProcessInstance() {
		String processDefinitionKey = "LeaveProcess"; // 流程定义的key对应bpmn文件中id的属性值
		ProcessInstance processInstance = processEngine.getRuntimeService() // 获得与正在执行的流程实例和执行对象相关的Service
			.startProcessInstanceByKey(processDefinitionKey); // 根据流程定义的key启动流程实例，默认是按照最新版本的流程定义启动
		log.info("启动流程成功，Id=" + processInstance.getId() + "，deploymentId=" + processInstance.getDeploymentId() + "，name=" + processInstance.getName() + "，processInstanceId=" + processInstance.getProcessInstanceId());
	}
	
	/**
	 * 查询当前人的个人任务
	 * 
	 * processEngine.getTaskService() // 获得与正在执行的任务管理相关的Service
	 */
	@Test
	public void findMypersonalTask() {
		String assignee = "王五"; // 张三  李四  王五
		List<Task> taskList = processEngine.getTaskService() // 获得与正在执行的任务管理相关的Service
			.createTaskQuery() // 创建任务查询对象
			/* 查询条件 （where条件） */
			.taskAssignee(assignee) // 指定任务办理人
//			.taskCandidateUser(candidateUser) // 组任务办理人
//			.processDefinitionId(processDefinitionId) // 流程定义Id
//			.processDefinitionKey(processDefinitionKey) // 流程定义key
//			.processInstanceId(processInstanceId) // 流程实例Id
//			.executionId(executionId) // 使用执行对象Id查询
			
			/* 排序条件 */
			.orderByTaskCreateTime().asc() // 创建时间升序 
			
			/* 返回的结果集 */
			.list(); // 完成查询
		
		if(null == taskList || taskList.size() == 0) {
			log.error("没有查询到" + assignee + "的个人任务！");
			return;
		}
		
		for(Task task : taskList) {
			log.info("任务Id=" + task.getId() + "，任务名称=" + task.getName() + "，任务创建时间=" + task.getCreateTime() + "，流程实例Id=" + task.getProcessInstanceId() + "，执行的对象Id=" + task.getExecutionId() + "，流程定义Id=" + task.getProcessDefinitionId());
		}
	}
	
	/**
	 * 完成我的任务
	 * 完成任务后数据库表的变化
	 *  -> `act_ge_property`配置表中会更新数据，下次insert数据的Id
	 *  -> `act_hi_taskinst`历史任务表中会插入数据，流程定义Id，任务定义Id，流程实例Id，执行对象Id，任务名称，任务办理人，任务开始时间等信息
	 *  -> `act_hi_actinst`历史活动实例表中插入数据，流程定义Id，流程实例Id，执行对象Id，活动定义Id，任务Id，活动定义名称，任务办理人，开始结束时间等信息
	 *  -> `act_hi_identitylink`历史办理人表插入数据，组Id，办理人类型，办理人Id，流程实例Id等信息
	 *  -> `act_ru_task`正在执行的任务表插入数据，任务Id，执行对象Id，流程实例Id，流程定义Id，当前任务名称，当前任务定义Id，当前任务办理人，创建时间等信息
	 *  -> `act_ru_identitylink`正在执行的办理人表插入数据，组Id，办理人类型，办理人Id，流程实例Id等信息
	 *  -> `act_ru_execution`正在执行的执行对象表执行更新，更新当前执行的活动定义Id
	 *  -> `act_hi_actinst`历史活动实例表执行更新，更新任务结束时间
	 *  -> `act_hi_taskinst`历史任务表执行更新，更新任务结束时间
	 *  -> `act_ru_task`正在执行的任务表执行删除，删除上一条任务
	 * 
	 * processEngine.getTaskService() // 获得与正在执行的任务管理相关的Service
	 */
	@Test
	public void completeMyPersonalTask() {
		String taskId = "30002";
		processEngine.getTaskService() // 获得与正在执行的任务管理相关的Service
			.complete(taskId);
		log.info("完成任务：任务Id=" + taskId);
	}
	
	/**
	 * 查询流程状态-判断流程正在进行还是结束
	 */
	@Test
	public void isProcessEnd() {
		String processInstanceId = "25001"; // 流程实例Id
		ProcessInstance processInstance = processEngine.getRuntimeService() // 与正在执行的流程实例和执行对象相关的Service
			.createProcessInstanceQuery() // 创建一个流程实例查询
			.processInstanceId(processInstanceId) // 使用流程实例Id查询
			.singleResult();
		
		if(processInstance == null) {
			log.info("流程已经结束");
		} else {
			log.info("流程没有结束");
		}
	}
	
	/**
	 * 查询历史任务
	 */
	@Test
	public void findHistoryTask() {
		String assignee = "张三"; // 历史任务办理人
		List<HistoricTaskInstance> hTaskList = processEngine.getHistoryService() // 与历史数据（历史表）相关的service
			.createHistoricTaskInstanceQuery() // 创建历史任务实例查询
			.taskAssignee(assignee) // 指定历史任务的办理人
			.list();
		
		if(null == hTaskList || hTaskList.size() == 0) {
			log.error("没有查询到" + assignee + "的历史任务！");
			return;
		}
		
		for(HistoricTaskInstance ht : hTaskList) {
			log.info("任务Id=" + ht.getId() + "，任务名称=" + ht.getName() + "，流程实例Id=" + ht.getProcessInstanceId() + "，开始时间=" + ht.getStartTime() + "，结束时间=" + ht.getEndTime());
		}
	}
	
	/**
	 * 查询历史流程实例
	 */
	@Test
	public void findHistoryProcessInstance() {
		String processInstanceId = "25001"; // 流程实例Id
		HistoricProcessInstance hpi = processEngine.getHistoryService() // 与历史数据（历史表）相关的service
			.createHistoricProcessInstanceQuery() // 创建历史流程实例查询
			.processInstanceId(processInstanceId) // 使用流程实例Id查询
			.singleResult(); // 返回唯一结果集
		
		if(null == hpi) {
			log.error("没有查询到processInstanceId：" + processInstanceId + "对应的历史流程实例！");
			return;
		}
		
		log.info("流程实例Id：" + hpi.getId() + "，流程实例名称：" + hpi.getName() + "，流程定义Id：" + hpi.getProcessDefinitionId() + "，流程实例开始时间：" + hpi.getStartTime() + "，流程实例结束时间：" + hpi.getEndTime());
	}

}
